{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import os\n",
    "from dotenv import load_dotenv\n",
    "# Load environment variables from openai.env file\n",
    "load_dotenv(\"openai.env\")\n",
    "\n",
    "# Read the OPENAI_API_KEY from the environment\n",
    "api_key = os.getenv(\"OPENAI_API_KEY\")\n",
    "api_base = os.getenv(\"OPENAI_API_BASE\")\n",
    "os.environ[\"OPENAI_API_KEY\"] = api_key\n",
    "os.environ[\"OPENAI_API_BASE\"] = api_base"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 四种文档处理链\n",
    "- stuff\n",
    "- refine\n",
    "- Map reduce\n",
    "- Map re-mark\n",
    "\n",
    "### Map reduce\n",
    "先将每个文档或文档块分别投喂给LLM，并得到结果集（Map步骤），然后通过一个文档合并链，获得一个输出结果（Reduce步骤）\n",
    "![Alt Text](map.png)\n",
    "![Alt Text](reduce.png)\n",
    "<hr>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/tomiezhang/.pyenv/versions/3.10.12/envs/langchains/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The class `langchain_community.chat_models.openai.ChatOpenAI` was deprecated in langchain-community 0.0.10 and will be removed in 0.2.0. An updated version of the class exists in the langchain-openai package and should be used instead. To use it run `pip install -U langchain-openai` and import as `from langchain_openai import ChatOpenAI`.\n",
      "  warn_deprecated(\n"
     ]
    }
   ],
   "source": [
    "from langchain.chains import MapReduceDocumentsChain\n",
    "from langchain.chains import ReduceDocumentsChain\n",
    "from langchain.chains.combine_documents.stuff import StuffDocumentsChain\n",
    "from langchain.prompts import PromptTemplate\n",
    "from langchain.chat_models import ChatOpenAI\n",
    "from langchain.chains import LLMChain\n",
    "from langchain.document_loaders import PyPDFLoader\n",
    "from langchain.text_splitter import CharacterTextSplitter\n",
    "\n",
    "#load pdf\n",
    "loader = PyPDFLoader(\"loader.pdf\")\n",
    "docs = loader.load()\n",
    "#split text\n",
    "text_splitter = CharacterTextSplitter.from_tiktoken_encoder(\n",
    "    chunk_size=1000,\n",
    "    chunk_overlap=0,\n",
    ")\n",
    "split_docs = text_splitter.split_documents(docs)\n",
    "#print(split_docs)\n",
    "\n",
    "#map chain\n",
    "map_template = \"\"\"对以下文字做简洁的总结:\n",
    "\"{content}\"\n",
    "简洁的总结:\"\"\"\n",
    "map_prompt = PromptTemplate.from_template(map_template)\n",
    "llm = ChatOpenAI(\n",
    "    temperature=0,\n",
    "    model=\"gpt-3.5-turbo\",\n",
    ")\n",
    "map_chain = LLMChain(\n",
    "    llm=llm,\n",
    "    prompt=map_prompt,\n",
    ")\n",
    "\n",
    "#reduce chain\n",
    "reduce_template = \"\"\"以下是一个摘要集合:\n",
    "{doc_summaries}\n",
    "将上述摘要与所有关键细节进行总结.\n",
    "总结:\"\"\"\n",
    "reduce_prompt = PromptTemplate.from_template(reduce_template)\n",
    "reduce_chain = LLMChain(\n",
    "    prompt=reduce_prompt,\n",
    "    llm=llm,\n",
    ")\n",
    "stuff_chain = StuffDocumentsChain(\n",
    "    llm_chain=reduce_chain,\n",
    "    document_variable_name=\"doc_summaries\",\n",
    ")\n",
    "reduce_final_chain = ReduceDocumentsChain(\n",
    "    combine_documents_chain=stuff_chain,\n",
    "    #超过4000个token就会切入到下一个stuff_chain\n",
    "    collapse_documents_chain=stuff_chain,\n",
    "    token_max=4000,\n",
    ")\n",
    "\n",
    "#map reduce chain\n",
    "map_reduce_chain = MapReduceDocumentsChain(\n",
    "    llm_chain=map_chain,\n",
    "    document_variable_name=\"content\",\n",
    "    reduce_documents_chain=reduce_final_chain,\n",
    ")\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/tomiezhang/.pyenv/versions/3.10.12/envs/langchains/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The function `run` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "蒂法是《最终幻想VII》系列中的虚构角色，是克劳德的青梅竹马，拥有格斗技能，是雪崩组织成员。她与克劳德有特殊关系，曾帮助他康复并一同击败敌人。蒂法展现出坚强、独立和有吸引力的形象，是备受关注的角色之一。她在游戏中具有强大的情感和战斗能力，被多家媒体评为电子游戏史上最伟大的女性角色之一，具有永恒吸引力。她的名字象征着爱情、美丽和自我牺牲，是备受好评的经典角色之一。\n"
     ]
    }
   ],
   "source": [
    "summary = map_reduce_chain.run(split_docs)\n",
    "print(summary)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Map re-rank\n",
    "\n",
    "先将每个文档或文档块投喂给LLM,并对每个文档或文档块生成问题的答案进行打分，然后将打分最高的文档或文档块作为最终答案返回\n",
    "<hr>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.document_loaders import PyPDFLoader\n",
    "from langchain.text_splitter import CharacterTextSplitter\n",
    "from langchain.chat_models import ChatOpenAI\n",
    "from langchain.chains.qa_with_sources import load_qa_with_sources_chain\n",
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "llm = ChatOpenAI(temperature=0, model_name=\"gpt-3.5-turbo-16k\")\n",
    "#load\n",
    "loader = PyPDFLoader(\"loader.pdf\")\n",
    "docs = loader.load()\n",
    "#split\n",
    "text_splitter = CharacterTextSplitter.from_tiktoken_encoder(\n",
    "    chunk_size=500, chunk_overlap=0\n",
    ")\n",
    "split_docs = text_splitter.split_documents(docs)\n",
    "\n",
    "chain = load_qa_with_sources_chain(\n",
    "    ChatOpenAI(temperature=0), \n",
    "    chain_type=\"map_rerank\", \n",
    "    metadata_keys=['source'], \n",
    "    return_intermediate_steps=True\n",
    "    )\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "llm_chain=LLMChain(prompt=PromptTemplate(input_variables=['context', 'question'], output_parser=RegexParser(regex='(.*?)\\\\nScore: (\\\\d*)', output_keys=['answer', 'score']), template=\"Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.\\n\\nIn addition to giving an answer, also return a score of how fully it answered the user's question. This should be in the following format:\\n\\nQuestion: [question here]\\nHelpful Answer: [answer here]\\nScore: [score between 0 and 100]\\n\\nHow to determine the score:\\n- Higher is a better answer\\n- Better responds fully to the asked question, with sufficient level of detail\\n- If you do not know the answer based on the context, that should be a score of 0\\n- Don't be overconfident!\\n\\nExample #1\\n\\nContext:\\n---------\\nApples are red\\n---------\\nQuestion: what color are apples?\\nHelpful Answer: red\\nScore: 100\\n\\nExample #2\\n\\nContext:\\n---------\\nit was night and the witness forgot his glasses. he was not sure if it was a sports car or an suv\\n---------\\nQuestion: what type was the car?\\nHelpful Answer: a sports car or an suv\\nScore: 60\\n\\nExample #3\\n\\nContext:\\n---------\\nPears are either red or orange\\n---------\\nQuestion: what color are apples?\\nHelpful Answer: This document does not answer the question\\nScore: 0\\n\\nBegin!\\n\\nContext:\\n---------\\n{context}\\n---------\\nQuestion: {question}\\nHelpful Answer:\"), llm=ChatOpenAI(client=<openai.resources.chat.completions.Completions object at 0x126b60d90>, async_client=<openai.resources.chat.completions.AsyncCompletions object at 0x12788ac50>, temperature=0.0, openai_api_key='sk-pQ7osw5XrDY1Ejsx99EcEe5f58D04dDeB387B8E6B3C10bDb', openai_api_base='https://ai-yyds.com/v1', openai_proxy='https://ai-yyds.com/v1')) document_variable_name='context' rank_key='score' answer_key='answer' metadata_keys=['source'] return_intermediate_steps=True\n"
     ]
    }
   ],
   "source": [
    "print(chain)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"\\nUse the following pieces of context to answer the question in chinese at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.\\n\\n\\nIn addition to giving an answer, also return a score of how fully it answered the user's question. This should be in the following format:\\n\\n\\nQuestion: [question here]\\n\\nHelpful Answer: [answer here]\\n\\nScore: [score between 0 and 100]\\n\\n\\nHow to determine the score:\\n\\n- Higher is a better answer\\n\\n- Better responds fully to the asked question, with sufficient level of detail\\n\\n- If you do not know the answer based on the context, that should be a score of 0\\n\\n- Don't be overconfident!\\n\\n\\nExample #1\\n\\n\\nContext:\\n\\n---------\\n\\nApples are red\\n\\n---------\\n\\nQuestion: what color are apples?\\n\\nHelpful Answer: red\\n\\nScore: 100\\n\\n\\nExample #2\\n\\n\\nContext:\\n\\n---------\\n\\nit was night and the witness forgot his glasses. he was not sure if it was a sports car or an suv\\n\\n---------\\n\\nQuestion: what type was the car?\\n\\nHelpful Answer: a sports car or an suv\\n\\nScore: 60\\n\\n\\nExample #3\\n\\n\\nContext:\\n---------\\n\\nPears are either red or orange\\n\\n---------\\n\\nQuestion: what color are apples?\\n\\nHelpful Answer: This document does not answer the question\\n\\nScore: 0\\n\\n\\nBegin!\\n\\n\\nContext:\\n\\n---------\\n\\n{context}\\n\\n---------\\n\\nQuestion: {question}\\n\\nHelpful Answer:\""
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"\"\"\n",
    "Use the following pieces of context to answer the question in chinese at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.\\n\\n\n",
    "In addition to giving an answer, also return a score of how fully it answered the user's question. This should be in the following format:\\n\\n\n",
    "Question: [question here]\\n\n",
    "Helpful Answer: [answer here]\\n\n",
    "Score: [score between 0 and 100]\\n\\n\n",
    "How to determine the score:\\n\n",
    "- Higher is a better answer\\n\n",
    "- Better responds fully to the asked question, with sufficient level of detail\\n\n",
    "- If you do not know the answer based on the context, that should be a score of 0\\n\n",
    "- Don't be overconfident!\\n\\n\n",
    "Example #1\\n\\n\n",
    "Context:\\n\n",
    "---------\\n\n",
    "Apples are red\\n\n",
    "---------\\n\n",
    "Question: what color are apples?\\n\n",
    "Helpful Answer: red\\n\n",
    "Score: 100\\n\\n\n",
    "Example #2\\n\\n\n",
    "Context:\\n\n",
    "---------\\n\n",
    "it was night and the witness forgot his glasses. he was not sure if it was a sports car or an suv\\n\n",
    "---------\\n\n",
    "Question: what type was the car?\\n\n",
    "Helpful Answer: a sports car or an suv\\n\n",
    "Score: 60\\n\\n\n",
    "Example #3\\n\\n\n",
    "Context:\\n---------\\n\n",
    "Pears are either red or orange\\n\n",
    "---------\\n\n",
    "Question: what color are apples?\\n\n",
    "Helpful Answer: This document does not answer the question\\n\n",
    "Score: 0\\n\\n\n",
    "Begin!\\n\\n\n",
    "Context:\\n\n",
    "---------\\n\n",
    "{context}\\n\n",
    "---------\\n\n",
    "Question: {question}\\n\n",
    "Helpful Answer:\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/tomiezhang/.pyenv/versions/3.10.12/envs/langchains/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n",
      "/Users/tomiezhang/.pyenv/versions/3.10.12/envs/langchains/lib/python3.11/site-packages/langchain/chains/llm.py:344: UserWarning: The apply_and_parse method is deprecated, instead pass an output parser directly to LLMChain.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input_documents': [Document(page_content='蒂法介绍蒂法·洛克哈特（⽇语：ティファ・ロックハート，Tifa Rokkuhāto，英语：Tifa Lockhart）为电⼦游戏《最终幻想VII》及《最终幻想VII补完计划》相关作品中的虚构⻆⾊，由野村哲也创作和设计，此后也在多个游戏中客串登场。2014年东京电玩展上，星名美津纪cosplay《最终幻想VII 降临之⼦》中的蒂法·洛克哈特蒂法是克劳德的⻘梅⽵⻢，两⼈同为尼布鲁海姆出身。在⽶德加经营作为反抗组织“雪崩”根据地的酒馆“第七天堂”，并且是⼩有名⽓的招牌店员。擅⻓格⽃，以拳套为武器。本传7年前克劳德离开故乡从军时，曾许下约定“如果有危机时⼀定会保护她”。与爱丽丝相识之后，两⼈成为好友。第⼀个察觉克劳德记忆混乱的⼈，后来协助精神崩溃的克劳德重新找回真正的⾃⼰。本传的⼤战结束后，依⼤家的期待在战后新⽣的⽶德加再次开设第七天堂（原第七天堂因第柒区圆盘崩塌遭压毁），同时也照顾⼀群受到星痕症候群折磨的孩⼦们。蒂法被《纽约时报》称为“⽹络⼀代”的海报⼥郎，与劳拉·克罗夫特相⽐，她是电⼦游戏中坚强、独⽴和有吸引⼒的⼥性⻆⾊的典型代表。媒体普遍称赞其实⼒和外表，并称她为游戏世界中最好的⼥性⻆⾊之⼀。在《最终幻想VII》本传中，蒂法年龄20岁、身⾼167cm、⽣⽇5⽉3⽇、⾎型B型、出⽣地尼布尔海姆。登场《最终幻想VII》蒂法在《最终幻想VII》原版中⾸次亮相，是克劳德的⻘梅⽵⻢、第七天堂酒吧的看板娘、极端环境组织“雪崩”成员，该组织反抗巨型企业“神罗”因其⼤量抽取魔晄⽤作动⼒能源。在注意到克劳德的性格改变后，她说服克劳德加⼊雪崩，以密切关注他，并且跟随他追寻游戏中的对⼿萨菲罗斯。她⽆法阻⽌克劳德被萨菲罗斯操纵，在他的精神崩溃后，她帮助克劳德康复，并且两⼈意识到彼此间的相互感觉，最后与伙伴们⼀同击败了萨菲罗斯。[2]在闪回中可知，⼉时的蒂法⼀直是村中⼩孩的⼈⽓王。在⺟亲过世后，思念⺟亲的蒂法决定沿着⼩路⾛到他们故乡尼布尔海姆附近的⼀座⼭上，认为这样就能⻅到过世的⺟亲，原本跟着蒂法的其他⼩孩都在半路上因害怕⽽放弃，唯独克劳德仍坚定的在后⾯跟随，希望能在危机时保护蒂法。然⽽，他们俩都从⼭上跌落受伤，蒂法昏迷了⼀个星期，她的⽗亲认为克劳德对此负有责任[3]，甚为严令禁⽌克劳德再接近蒂法，但蒂法反⽽从此更在意克劳德，两⼈成为要好的玩伴。为了使⾃⼰变得更强⼤，克劳德最终选择离开尼布尔海姆，加⼊神罗，想要成为神罗的精英战⼠“神罗战⼠”（SOLDIER），但后来透露他主要是为了吸引蒂法的注意⼒。离开之前，蒂法与克劳德约定，当蒂法处于困境之中时，克劳德会回来救她。从克劳德离开之后，蒂法便开始留意神罗战⼠的消息，因为神罗战⼠都成为声名远播的知名⼈物，如果克劳德成为神罗战⼠，他的活跃也会⽴刻传回尼布尔海姆。数年后，在萨菲罗斯摧毁了尼布尔海姆之后，克劳德为了救蒂法，被萨菲罗斯刺⾄重伤。蒂法被她的武术教练赞⼲带到安全地带，幸存下来，最终到达⽶德加并遇⻅了“雪崩”的领导⼈巴雷特·华莱⼠。病愈后，蒂法加⼊了“雪崩”，为了给家乡被毁⼀事报仇。⼀天，她在⽕⻋站遇到了从魔晄炉中逃出来、精神⼀⽚混乱的克劳德，蒂法说服了他为巴雷特⼯作，以保证克劳德的安全以及和克劳德保持紧密关系。这是游戏开始的地⽅。在原版《最终幻想VII》中蒂法与爱丽丝关系友好，但会在某些时候争⻛吃醋，例如在神罗总部营救爱丽丝时，蒂法及巴雷特等⼀⾏失⼿被擒，若克劳德选择关⼼爱丽丝的话蒂法的对话中明显带有妒忌。在重制版中虽然删去这段情节，但保留了这种关系。在《最终幻想VII》的初稿中，蒂法是背景⼈物。她在“雪崩”中的作⽤是在幕后⽀持，在执⾏任务后为所有⼈加油⿎劲，并且对克劳德有特别的关⼼。据推测，她的背上有⼀块⼤的疤', metadata={'source': 'loader.pdf', 'page': 0}),\n",
       "  Document(page_content='痕，是由克劳德造成的，并在事件发⽣时因巨⼤冲击⽽部分失忆。[4]原版策划者之⼀的加藤正⼈提出了⼀个旨在暗示蒂法和克劳德发⽣性关系的场景，但被北濑佳范⽤⼀个变淡的⾊调所取代。野岛⼀成在接受采访时说，没有⼀个开发团队⼈员认为当时的场景会成为⼀个问题。[5]《最终幻想VII补完计划》2005年，蒂法出现在CG电影《最终幻想VII 降临之⼦》中，故事发⽣在原版游戏剧情结束两年后。在其中，她试图给予克劳德情感上的⽀持，敦促克劳德放下他对⾃⼰施加的不必要的罪恶感。此外，她还照顾巴雷特的养⼥玛琳和克劳德在爱丽丝的教堂发⻔⼝救下的孩⼦丹泽尔。在电影中，她与萨菲罗斯的其中⼀个思念体罗兹战⽃，后来她帮助与被召唤的⽣物巴哈姆特战⽃。编剧野岛⼀成将她在视频中的⻆⾊描述为“⾮常像任何被男⼈抛弃的⼥⼈”，并表示尽管他们不希望她显得笨拙，但他们也想描绘出从她受到克劳德的情感伤害出发。[6]在视频的初稿中，她原本计划在当时的短⽚中扮演更重要的⻆⾊，该短⽚仅以蒂法，克劳德和⼏个孩⼦为主。蒂法也在前传游戏《最终幻想VII 危机之前》和《最终幻想VII 核⼼危机》以及OVA《最终幻想VII 最终命令》中登场。每次登场时，她的出现都与尼布尔海姆的毁灭有关。[2]官⽅⼩说《通向微笑之路》中有专⻔的《蒂法篇》，讲述了原版游戏和降临之⼦两段之间的故事。从蒂法的⻆度出发，详细讲述了她如何在Edge City创建⼀个新的第七天堂酒吧，并试图坚持⾃⼰和克劳德的正常家庭观念，尽管克劳德逐渐开始逃避与他⼈接触。蒂法还短暂出现在游戏《最终幻想VII 地狱⽝的挽歌》中，该游戏的剧情在降临之⼦故事⼀年后发⽣，她在游戏中帮助主⻆⽂森特·瓦伦丁捍卫星球，抵抗怪物欧⽶茄和“兵器”。她后来出现在游戏的结尾中，讨论着⽂森特的失踪。[2]其他登场在《最终幻想VII补完计划》之外，蒂法还出现在格⽃游戏《神佑擂台》中，作为可解锁的⻆⾊和可选的Boss。[7]她后来出现在电⼦棋盘游戏《富豪街》中。在《王国之⼼II》中，她穿着⾃⼰在降临之⼦中的服装，寻找克劳德，然后与该系列的怪物“⽆⼼”战⽃。她原本计划出现在原版《王国之⼼》的最终合辑中，但由于时间限制，⼯作⼈员选择改⽤萨菲罗斯。[8]蒂法也是格⽃游戏《最终幻想 纷争012》中的玩家⻆⾊之⼀，该游戏的⻆⾊来⾃各种《最终幻想》游戏。[9]她身着《最终幻想VII》中的服装，但该玩家还可以使⽤她在《降临之⼦》中的服装和在尼布尔海姆出场时展示的第三种服装。[10][11]游戏的第⼀版展示内容是在天野喜孝的美术原型基础上展现的另⼀种形式。[12]在《⼩⼩⼤星球2》中，蒂法是可下载的⻆⾊模型。[13]韩国歌⼿Ivy在2007年的歌曲“ਬ\\u0d11\\u0a44ਬ\\u0d11\\u0a44”（《诱惑奏鸣曲》）MV中描绘了⻆⾊。因为重现了《降临之⼦》中的战⽃场⾯，在史克威尔艾尼克斯提出版权诉讼后，该视频被禁⽌在韩国电视上播出。[14] 2015年，蒂法作为可玩⻆⾊被添加到iOS游戏《最终幻想 记录者》中。[15]创作与发展蒂法在《最终幻想VII 核⼼危机》中的形象。蒂法由野村哲也设计，最初版本的《最终幻想VII》中没有蒂法，因为最初该游戏只有三个可玩⻆⾊。主⻆克劳德、爱丽丝和巴雷特。但是，在给游戏总监北濑佳范打电话时，有⼈建议游戏中的某个主⻆应该死掉，在对究竟是巴雷特还是爱丽丝需要死掉进⾏⼤量讨论之后，制⽚⼈选择了爱丽丝。[3]野村哲也后来开玩笑说这是他的主意，以便使他能够将蒂法引⼊游戏中。[16]⽆论如何，北濑佳范喜欢有两个的⼥主⼈公，并且在他们之间摇摆不定，将其称之为是', metadata={'source': 'loader.pdf', 'page': 1}),\n",
       "  Document(page_content='《最终幻想》系列中的新事物。[5]野村哲也将蒂法的⻆⾊在《降临之⼦》中的表现分为多个⽅⾯，称她“像⺟亲，爱⼈，战⽃中的亲密盟友”，“不仅在情感上，⽽且在身体上也⾮常坚强”。[6]蒂法被设计为该系列以前的游戏中出现的“Monk”级别的⻆⾊。她有⼀头乌⿊的⻓发，像⼀头海豚的尾巴，穿着简单单调的服装，包括⽩⾊的上⾐和⿊⾊的超短裙。她还穿着红⾊的靴⼦和⼿套，⿊⾊的袖⼦从⼿腕延伸到肘部。她的裙⼦被⼀对狭窄的⿊⾊吊带裤撑起，⼤⾯积的⾦属护罩覆盖了她的左肘。她身⾼约167厘⽶[17]，三围为92-60-88厘⽶。[4]最初，野村哲也很难决定选择搭配迷你裙还是搭配⻓裤。为了征求意⻅，他在史克威尔的办公室传递了草图，⼤多数⼯作⼈员都认同超短裙设计。[16]这与爱丽丝的标志形成鲜明对⽐，爱丽丝的⻓裙是她的标志。服装在游戏中被解释为赋予了她⾏动⾃由的能⼒，这是由于她的近距离战⽃的亲和⼒，⽽裙⼦据称“相当短，具有相当⼤的曝光度”[4]。开发者还指出，由于她的身材，她穿着其他便⾐的外观也令⼈愉悦。[4]在制作《最终幻想VII 降临之⼦》时，联合导演野末武志难以为蒂法的身材构建出⼀个“平衡，但⼜展现了她的⼥性特质”的框架。在这⼀点上，她的服装也进⾏了重新设计，着重于表达这些品质，同时仍然令⼈赏⼼悦⽬。⽩⾊背⼼覆盖⿊⾊背⼼，⽩⾊缎带背⼼包裹着她的左⼆头肌，粉红丝带包裹着她的脚。⿊⾊系扣裙⼦遮盖了她的⼤腿，并且在下⾯穿了短裤，⽤⼀块类似于⼤⾐的布从裙⼦的腰带后部延伸到脚踝。她不再使⽤吊带撑起裙⼦，只在电影的战⽃场景中戴着⼿套。她的发型被更改为在她的背部中部结束，从原来的设计中删除了海豚的尾巴。[6]造成这种变化的原因是，难以为她的原始头发设置动画效果，以及由于其⿊⾊和亮光⽽引起的问题。另外，瞳孔的颜⾊亦由红⾊改为深褐⾊。蒂法在《最终幻想VII 重制版》中的形象。开发《最终幻想VII 重制版》时，史克威尔修改了蒂法的原始外观，以使她的外观更加逼真，因为⼯作⼈员意识到她的设计不适合战⽃场景。她因此得到了⿊⾊运动内⾐并贴合她的身体，给她⼀种运动的感觉。[18]在这个版本中的蒂法虽然保留着“海豚尾巴”发型，但原版中其末端如海豚尾鳍已变为更⾃然的垂直模样。另外,在第三章中,玩家可选三种服饰中的⼀种让蒂法在以后的篇章穿上。除了“成熟⻛”、“格⽃家⻛”及“异国⻛”外，开发团队亦考虑过“⼥仆⻛”、“警察服”、“舞蹈家⻛”、“陆⾏⻦⻛”等[19]。由于克劳德与蒂法和爱丽丝之间存在的特殊关系，开发团队观察到粉丝认为史克威尔更偏爱这两个⼥主⻆。以往，史克威尔表示爱丽丝可能是游戏的真正主⻆，⽽蒂法对于帮助克劳德这个⻆⾊的发展很重要。最终，史克威尔表示蒂法和爱丽丝都是这次重制版中的⼥主⻆。[20]野村哲也表示，他喜欢⼥演员伊藤步，并希望与她在《降临之⼦》中合作。在爱丽丝的配⾳演员已经决定之后，野村让伊藤步为蒂法配⾳，觉得她的“沙哑的声⾳”将与坂本真绫配⾳的轻声细语的爱丽丝形成很好的对⽐。[6]野村哲也还指出，在完成蒂法的更新设计后，制⽚⼈对她的最终细节进⾏了辩论，但伊藤步饰演⻆⾊后，便选择将配⾳演员的许多特征融合到⻆⾊的最终形象中。[6]英国配⾳演员瑞秋·莱·寇克（Rachael Leigh Cook）在《王国之⼼II》的⼀次采访中说，她喜欢玩蒂法，并形容她“身体和情感上都很强壮，但也⾮常敏感”，并且“⾮常多维”。[21]在给⻆⾊配⾳时，寇克听取了伊藤步的配⾳。在《降临之⼦》制作之后，寇克感谢野村哲也所创作的视频并表示⾮常喜欢。[22]布⾥特·巴伦（Britt Baron）代替她参加了《最终幻想VII 重制版》的配⾳。[23]蒂法幼年时候由Glory Curda配⾳。[24]评价⾃登场以来，蒂法受到评论家和粉丝的极⼤好评。在2000年，GameSpot的读者将她评为电⼦游戏中的第五好的⼥性⻆⾊，该⽹站的编辑指出他们对此表示同意。[25] 2004年，', metadata={'source': 'loader.pdf', 'page': 2}),\n",
       "  Document(page_content=\"《Play》杂志在《游戏⼥孩》年度期刊的第⼀期中介绍了蒂法，并称她为“近代史上最受崇拜的⼥性。”2007年，蒂法被《电击PlayStation》评为在原版PlayStation平台上有史以来第⼋好的⼈物，也是《最终幻想VII》中排名第三的⻆⾊。[26]同年，Tom's Hardware将她列为视频游戏历史上50个最伟⼤的⼥性⻆⾊之⼀，并称她为“周围⼈物中更丰富，更复杂的⼥性⻆⾊之⼀。”[27]在2008年，UGO将她列为电⼦游戏史上最伟⼤的⼥性⻆⾊之⼀，排在第五位，并表示对她的偏好超过了爱丽丝，并补充说：“ 蒂法的服装是轻描淡写的奇迹-但正是她的天⽣丽质和令⼈难忘的性格使她在榜单上名列前茅。”[28]同年，Chip杂志将她列为“游戏⼥孩”第⼗名。[29]2009年，IGN将蒂法列为游戏中⼗⼤最佳⼥主⻆之⼀，并称她为“毫⽆疑问的最终幻想宇宙中的传奇⼥主⻆。”[30] Fami通在2010年进⾏的⼀项⺠意调查将她评为第19⼤最受⽇本玩家欢迎的视频游戏⻆⾊。[31]在2013年，Complex将她列为电⼦游戏历史上第13⼤⼥主⻆。[32]2001年，随着劳拉·克罗夫特的推出，Beaumont Enterprise列举了蒂法作为视频游戏中强⼤⼥性⻆⾊的榜样。2008年，Joystiq将她列为他们希望在史克威尔艾尼克斯的跨界格⽃游戏《最终幻想 纷争》中看到的《最终幻想》系列中20个⻆⾊中的佼佼者，并将她描述为该系列“最伟⼤的⼥主⻆”之⼀。[33]IGN在2008年将蒂法列⼊有史以来第13部最佳的《最终幻想》⻆⾊之⼀，描述她是史克威尔试图“赋予《最终幻想》⻆⾊真实的性感”的尝试，以及“可以在紧要关头照顾好⾃⼰”的⼈；在后续的读者选择榜单中，蒂法排名第⼀，⼯作⼈员在将其在榜单上的位置归因于她的胸部时重复了先前的评论。[34]在2009年IGN的⼀篇⽂章中，仅关注《最终幻想VII》中的⻆⾊，她排在第四位，并评论说，尽管她的性感提⾼了她的知名度，但“ 蒂法推动了坚强，独⽴的RPG⼥主⻆的传统。”[35]其他杂志也称赞蒂法的性格特征。Mania Entertainment在2010年“了不起的视频游戏⼥性”名单中，蒂法排名第⼗，并指出，虽然《最终幻想》系列的后续游戏引⼊了其他令⼈难忘的⼥性⻆⾊，但“蒂法是我们的第⼀个《最终幻想》⼥孩，并在我们⼼中有特殊的地位”。[36] 2013年，《Complex》的Gus Turner将蒂法列为有史以来⼗⼆⼤最终幻想⻆⾊之⼀，并指出“除了劳拉和萨姆斯·艾仁之外，蒂法也代表了游戏中最独⽴，最有能⼒的⼥性之⼀。” [37]许多评论都认为蒂法⾮常性感。 1998年，《纽约时报》将她列为“⽹络⼀代”的炙⼿可热的海报⼥郎。[38]同年，《电⼦游戏⽉刊》授予她1997年的“最热⻔游戏宝⻉”，称她“⽐例匀称”，并称赞她是劳拉的另⼀可⾏选择。 UGO在2008年“电⼦游戏热⻔榜”中将她排在第24位，并补充说他们⽆法“克服之后的每个游戏版本中她的表现都更好”。[39]同年，GameDaily在“最热游戏辣妹”名单中将她排在第31位，分享了UGO对她的偏爱，并赞扬了她的外表和战⽃能⼒。[40]MSN也有类似的看法，当他们将“这个充满爱⼼，关怀，超级性感的⼥孩”列⼊“游戏界最热⻔的辣妹”名单中时，她名列第六，并指出她在该系列中的存在“有些微妙，给与了她更像是⼀种情感上的暗示。”⽽且没有她，这个系列就不会那么特别。[41]Manolith在2009年“最热⻔”⼥性视频游戏主⻆名单中将她排名第⼆。[42] 2010年，VideoGamer.com将她列为“⼗⼤电⼦游戏⼥神”，[43]⽽AfterEllen的塞拉·沃恩（Sarah Warn）将她评为“第九⼤最热”⼥性电⼦游戏⻆⾊。[44]2011年，Complex将她列为“游戏中最漂亮的⼥性⻆⾊”第16名，[45]⽽UGO仅因她在《神佑擂台》中的出现⽽将她列为“格⽃游戏中最优秀的⼥性”中的第13位。[46]同年，GameFront将她的胸部排在“电⼦游戏史上最伟⼤的胸部”名单的第九位，称她为“存在的危机版劳拉；” [47]她也被Village Voice Media列⼊“不可思议的胸部名单”，但评论说她“不仅仅具有性感。”[48]2012年，Complex将她列为整体上“第⼆热⻔”视频游戏⻆⾊，[49]MSN将她列为“电⼦游戏史上最热⻔的20位⼥性”之⼀，并补充说“她是历史上最著名的游戏⼥孩之⼀，并且具有永恒的吸引⼒。” [50]2013年，《每⽇纪事报》的Scott Marley将她排在“最有魅⼒的⼥性电⼦游戏⻆⾊”第⼆位，[51]⽽CheatCodes.com则宣布她是有史以来“最热⻔的⼥性\", metadata={'source': 'loader.pdf', 'page': 3}),\n",
       "  Document(page_content='电⼦游戏⻆⾊”。[52]同样，《La NuevaEspaña》在2014年将“性感，独⽴和坚强”的蒂法列为男⼥最性感的⼗⼤电⼦游戏⻆⾊中，[53]⽽《ThanhNiên》在2015年将她评为最性感的⼥性电⼦游戏⻆⾊。[54]姓名由来蒂法（Tifa）的英⽂名字来源于⼀个古希伯来语单词“Tiferet” ，⽽姓⽒洛克哈特（Lockhart）则是英语单词Lock和Hard的结合；Tiferet是⽣命之树的⼀个成分（⽣命之树⼤概可以分为三⽀柱、⼗个原质、四个世界、⼆⼗⼆路径等基本结构），象征着爱情、美丽和⾃我牺牲，从游戏⾥可以看出，这⼏点都符合Tifa的情况。', metadata={'source': 'loader.pdf', 'page': 4})],\n",
       " 'question': 'what is this document talk about?answer by chinese',\n",
       " 'source': 'loader.pdf',\n",
       " 'intermediate_steps': [{'answer': \"I don't know\", 'score': '0'},\n",
       "  {'answer': \"I don't know\", 'score': '0'},\n",
       "  {'answer': \"I don't know\", 'score': '0'},\n",
       "  {'answer': \"I don't know\", 'score': '0'},\n",
       "  {'answer': '这个文件讨论了电子游戏角色蒂法的性感和独立特质，以及她的名字来源和象征意义。', 'score': '90'}],\n",
       " 'output_text': '这个文件讨论了电子游戏角色蒂法的性感和独立特质，以及她的名字来源和象征意义。'}"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "query = \"what is this document talk about?answer by chinese\"\n",
    "result = chain({\"input_documents\":split_docs,\"question\":query})\n",
    "result"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchains",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
        